<!DOCTYPE html>
<html>
<!--
Copyright 2008 The Closure Library Authors. All Rights Reserved.

Use of this source code is governed by an Apache 2.0 License.
See the COPYING file for details.
-->
<head>
<title>Closure Unit Tests - goog.testing.Mock</title>
<script src="../base.js"></script>
<script>
  goog.require('goog.array');
  goog.require('goog.object');
  goog.require('goog.testing.Mock');
  goog.require('goog.testing.MockExpectation');
  goog.require('goog.testing.jsunit');
</script>
</head>
<body>
<script>

  // The object that we will be mocking
  var RealObject = function() {
  };

  RealObject.prototype.a = function() {
    fail('real object should never be called');
  };

  RealObject.prototype.b = function() {
    fail('real object should never be called');
  };

  var matchers = goog.testing.mockmatchers;
  var mock;

  function setUp() {
    var obj = new RealObject();
    mock = new goog.testing.Mock(obj);
  }

  function testMockErrorMessage() {
    var expectation = new goog.testing.MockExpectation('a');
    assertEquals(0, expectation.getErrorMessageCount());
    assertEquals('', expectation.getErrorMessage());

    expectation.addErrorMessage('foo failed');
    assertEquals(1, expectation.getErrorMessageCount());
    assertEquals('foo failed', expectation.getErrorMessage());

    expectation.addErrorMessage('bar failed');
    assertEquals(2, expectation.getErrorMessageCount());
    assertEquals('foo failed\nbar failed', expectation.getErrorMessage());
  }

  function testVerifyArgumentList() {
    var expectation = new goog.testing.MockExpectation('a');
    assertEquals('', expectation.getErrorMessage());

    // test single string arg
    expectation.argumentList = ['foo'];
    assertTrue(mock.$verifyCall(expectation, 'a', ['foo']));

    // single numeric arg
    expectation.argumentList = [2]
    assertTrue(mock.$verifyCall(expectation, 'a', [2]));

    // single object arg (using standard === comparison)
    var obj = {prop1: 'prop1', prop2: 2};
    expectation.argumentList = [obj];
    assertTrue(mock.$verifyCall(expectation, 'a', [obj]));

    // make sure comparison succeeds if args are similar, but not ===
    var obj2 = {prop1: 'prop1', prop2: 2};
    expectation.argumentList = [obj];
    assertTrue(mock.$verifyCall(expectation, 'a', [obj2]));
    assertEquals('', expectation.getErrorMessage());

    // multiple args
    expectation.argumentList = ['foo', 2, obj, obj2];
    assertTrue(mock.$verifyCall(expectation, 'a', ['foo', 2, obj, obj2]));

    // test flexible arg matching.
    expectation.argumentList = ['foo', matchers.isNumber];
    assertTrue(mock.$verifyCall(expectation, 'a', ['foo', 1]));

    expectation.argumentList = [new matchers.InstanceOf(RealObject)];
    assertTrue(mock.$verifyCall(expectation, 'a', [new RealObject()]));
  }


  function testRegisterArgumentListVerifier() {
    var expectationA = new goog.testing.MockExpectation('a');
    var expectationB = new goog.testing.MockExpectation('b');

    // Simple matcher that return true if all args are === equivalent.
    mock.$registerArgumentListVerifier('a', function(expectedArgs, args) {
      return goog.array.equals(expectedArgs, args, function(a, b) {
        return (a === b);
      });
    });

    // test single string arg
    expectationA.argumentList = ['foo'];
    assertTrue(mock.$verifyCall(expectationA, 'a', ['foo']));

    // single numeric arg
    expectationA.argumentList = [2]
    assertTrue(mock.$verifyCall(expectationA, 'a', [2]));

    // single object arg (using standard === comparison)
    var obj = {prop1: 'prop1', prop2: 2};
    expectationA.argumentList = [obj];
    expectationB.argumentList = [obj];
    assertTrue(mock.$verifyCall(expectationA, 'a', [obj]));
    assertTrue(mock.$verifyCall(expectationB, 'b', [obj]));

    // if args are similar, but not ===, then comparison should succeed
    // for method with registered object matcher, and fail for method without
    var obj2 = {prop1: 'prop1', prop2: 2};
    expectationA.argumentList = [obj];
    expectationB.argumentList = [obj];
    assertFalse(mock.$verifyCall(expectationA, 'a', [obj2]))
    assertTrue(mock.$verifyCall(expectationB, 'b', [obj2]));


    // multiple args, should fail for method with registered arg matcher,
    // and succeed for method without.
    expectationA.argumentList = ['foo', 2, obj, obj2];
    expectationB.argumentList = ['foo', 2, obj, obj2];
    assertFalse(mock.$verifyCall(expectationA, 'a', ['foo', 2, obj2, obj]));
    assertTrue(mock.$verifyCall(expectationB, 'b', ['foo', 2, obj2, obj]));
  }


  function testCreateProxy() {
    mock = new goog.testing.Mock(RealObject, false, true);
    assertTrue(mock.$proxy instanceof RealObject);
    assertThrows(function() {
      new goog.testing.Mock(RealObject, true, true);
    });
    assertThrows(function() {
      new goog.testing.Mock(1, false, true);
    });
  }


  function testValidConstructorArgument() {
    var someNamespace = { };
    assertThrows(function() {
        new goog.testing.Mock(someNamespace.RealObjectWithTypo);
    });
  }


  function testArgumentsAsString() {
    assertEquals('()', mock.$argumentsAsString([]));
    assertEquals('(string, number, object, null)',
                 mock.$argumentsAsString(['red', 1, {}, null]));
  }


  function testThrowCallExceptionBadArgs() {
    var msg;
    mock.$throwException = function(m) {
      msg = m;
    };

    mock.$throwCallException(
        'fn1', [ 'b' ],
        { name: 'fn1',
          argumentList: [ 'c' ],
          getErrorMessage: function() { return ''; } });
    assertContains(
        'Bad arguments to fn1().\nActual: (string)\nExpected: (string)', msg);
  }

  function testThrowCallExceptionUnexpected() {
    var msg;
    mock.$throwException = function(m) {
      msg = m;
    };

    mock.$throwCallException('fn1', [ 'b' ]);
    assertEquals('Unexpected call to fn1(string).', msg);
  }

  function testThrowCallExceptionUnexpectedWithNext() {
    var msg;
    mock.$throwException = function(m) {
      msg = m;
    };

    mock.$throwCallException(
        'fn1', [ 'b' ],
        { name: 'fn2',
          argumentList: [ 3 ],
          getErrorMessage: function() { return ''; } });
    assertEquals(
        'Unexpected call to fn1(string).\n' +
        'Next expected call was to fn2(number)', msg);
  }

</script>
</body>
</html>
